// @Author EthanScriptOn
// @Desc
package resource

import (
	"errors"
	"gitee.com/fatzeng/srf_switch_basic_components/proxy/interceptor"
	"gitee.com/fatzeng/srf_switch_basic_components/tool"
)

// ResourceManager explorer
type ResourceManager struct {
	mu        *tool.ReentryLock
	container map[interface{}]interface{} // container container
}

// ResourceOptions Action items to manage resources
type ResourceOptions struct {
	PreOption   []*interceptor.InterceptorOptions                 // PreOption preOperation
	DoExecute   func(container map[interface{}]interface{}) error // DoExecute operation
	AfterOption []*interceptor.InterceptorOptions                 // AfterOption postOperation
	Interrupt   bool                                              // Interrupt break flags
}

func GenerateResourceManager() *ResourceManager {
	return &ResourceManager{
		container: make(map[interface{}]interface{}),
		mu:        tool.GenerateReentryLock(true),
	}
}

// interceptorRun intercept the action
func interceptorRun(isInterrupt bool, options []*interceptor.InterceptorOptions) (err error) {
	for _, option := range options {
		if isInterrupt {
			return
		}
		var ipt bool
		if ipt, err = option.IsPassThrough(); ipt {
			option.Call()
			if err = option.GetError(); err != nil {
				option.OnError(err)
				return err
			}
			if err = option.GetResultError(); err != nil {
				option.OnError(err)
				return err
			}
		} else {
			option.OnError(err)
			return err
		}
	}
	return
}

// OperateResource operations on resources
func (r *ResourceManager) OperateResource(resourceOptions *ResourceOptions) (err error) {
	return tool.LockReentryWithSync(r.mu, func() (err error) {
		if resourceOptions == nil {
			return errors.New("resource options is nil")
		}
		if err = interceptorRun(resourceOptions.Interrupt, resourceOptions.PreOption); err != nil {
			return err
		}
		if err = resourceOptions.DoExecute(r.container); err != nil {
			return err
		}
		if err = interceptorRun(resourceOptions.Interrupt, resourceOptions.AfterOption); err != nil {
			return err
		}
		return
	})
}

// ComputeIfAbsent 不存在则操作
func (r *ResourceManager) ComputeIfAbsent(identifier interface{}, option func(interface{}) interface{}) (interface{}, error, bool) {
	if option == nil {
		return nil, errors.New("option is nil"), false
	}
	var resp interface{}
	var hasResult bool
	return resp, tool.LockReentryWithSync(r.mu, func() (err error) {
		operator, ok := r.container[identifier]
		if !ok {
			hasResult = false
			resp = option(identifier)
			r.container[identifier] = resp
			return
		}
		resp = operator
		hasResult = true
		return
	}), hasResult
}

// ComputeIfPresent 存在则操作
func (r *ResourceManager) ComputeIfPresent(identifier interface{}, option func(interface{}) interface{}) (interface{}, error, bool) {
	if option == nil {
		return nil, errors.New("option is nil"), false
	}
	var resp interface{}
	var hasResult bool
	return resp, tool.LockReentryWithSync(r.mu, func() (err error) {
		_, ok := r.container[identifier]
		if !ok {
			hasResult = false
			return
		}
		hasResult = true
		optionRes := option(identifier)
		r.container[identifier] = optionRes
		resp = optionRes
		return
	}), hasResult
}

// GetResource 获取资源
func (r *ResourceManager) GetResource(identifier interface{}) (interface{}, error, bool) {
	var resp interface{}
	var hasResult bool
	return resp, tool.LockReentryWithSync(r.mu, func() (err error) {
		operator, ok := r.container[identifier]
		if !ok {
			return
		}
		resp = operator
		hasResult = true
		return
	}), hasResult
}

// DelResource 删除资源
func (r *ResourceManager) DelResource(identifier interface{}) (interface{}, error, bool) {
	var oldResp interface{}
	var hasResult bool
	return oldResp, tool.LockReentryWithSync(r.mu, func() (err error) {
		operator, ok := r.container[identifier]
		if !ok {
			return
		}
		delete(r.container, identifier)
		oldResp = operator
		hasResult = true
		return
	}), hasResult
}

// AddResource 添加资源
func (r *ResourceManager) AddResource(identifier interface{}, val interface{}) (interface{}, error, bool) {
	var oldResp interface{}
	var isAdded bool
	return oldResp, tool.LockReentryWithSync(r.mu, func() (err error) {
		operator, ok := r.container[identifier]
		if ok {
			oldResp = operator
		}
		r.container[identifier] = val
		isAdded = true
		return
	}), isAdded
}
